//
// Created by 麻再挺 on 2021/12/10.
//

#include "array.h"

/**
 * 若维数 dim 和随后的各维数长度合法, 则构造相应的数组 A, 并返回 OK
 * @param arr 数组
 * @param dim 维数
 * @return
 */
Status Array_Init(Array *arr, int dim, ...) {
    if (dim < 1 || dim > MAX_ARRAY_DIM) return ERROR;
    // 赋值维数
    arr->dim = dim;
    // 申请内存
    arr->bounds = malloc(dim * sizeof(int));
    if (!arr->bounds) exit(OVERFLOW);
    // 若各维度长度合法, 则存入 bounds, 并求出数组总数
    int elemTotal = 1;
    // 可变参数列表
    va_list ap;
    va_start(ap, dim);
    // 取值
    for (int i = 0; i < dim; ++i) {
        arr->bounds[i] = va_arg(ap, int);
        // 判断是否不小于 0
        if (arr->bounds[i] < 0) return UNDERFLOW;
        // 计算元素总数
        if (arr->bounds[i] > 0) elemTotal *= arr->bounds[i];
    }
    va_end(ap);
    // 申请内存
    arr->base = malloc(elemTotal * sizeof(AElemType));
    if (!arr->base) exit(OVERFLOW);
    // 求映像函数的常数 Ci
    arr->constants = malloc(dim * sizeof(int));
    // 指针的增减以元素的大小为单位
    arr->constants[dim - 1] = 1;
    for (int i = dim - 2; i >= 0; --i) {
        arr->constants[i] = arr->bounds[i + 1] * arr->constants[i + 1];
    }
    return OK;
}

/**
 * 销毁数组
 * @param arr 数组
 * @return
 */
Status Array_Destroy(Array *arr) {
    if (!arr->base) return ERROR;
    free(arr->base);
    arr->base = NULL;
    if (!arr->bounds) return ERROR;
    free(arr->bounds);
    arr->bounds = NULL;
    if (!arr->constants) return ERROR;
    free(arr->constants);
    arr->constants = NULL;
    return OK;
}

/**
 * 查询元素在数组中的相对地址
 * @param arr 数组
 * @param ap 参数列表
 * @param off 地址
 * @return
 */
Status Array_Locate(Array *arr, va_list ap, int *off) {
    for (int i = 0; i < arr->dim; ++i) {
        int index = va_arg(ap, int);
        if (index < 0 || index >= arr->bounds[i]) return OVERFLOW;
        *off += arr->constants[i] * index;
    }
    return OK;
}

/**
 * n 维数组,  elem 元素变量, 随后是 n 个下标的值
 * @param arr n 维数组
 * @param elem 元素变量
 * @return
 */
Status Array_Value(Array *arr, AElemType *elem, ...) {
    va_list ap;
    va_start(ap, elem);
    int result;
    int off = 0;
    if ((result = Array_Locate(arr, ap, &off)) <= 0) return result;
    // 元素值
    *elem = *(arr->base + off);
    return OK;
}

/**
 * n 维数组,  elem 元素变量, 随后要赋的 n 个下标的值
 * @param arr n 维数组
 * @param elem 元素变量
 * @return
 */
Status Array_Assign(Array *arr, AElemType elem, ...) {
    va_list ap;
    va_start(ap, elem);
    int result;
    int off = 0;
    if ((result = Array_Locate(arr, ap, &off)) <= 0) return result;
    // 元素值
    *(arr->base + off) = elem;
    return OK;
}

/**
 * 打印数组
 * @param arr 数组
 */
void Array_Print(Array *arr) {
    printf("bounds: ");
    int dimLen = arr->dim;
    for (int i = 0; i < dimLen; ++i) {
        printf("%d\t", arr->bounds[i]);
    }
    printf("\n");
    printf("constants: ");
    for (int i = 0; i < dimLen; ++i) {
        printf("%d\t", arr->constants[i]);
    }
    printf("\n");
}